package newcrunch.gui;

import edu.rice.cs.dynamicjava.interpreter.InterpreterException;
import interpreter.JavaInterpreter;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import newcrunch.util.Misc;

/**
 *
 * @author ccadm
 */
public class InterpreterManager 
{
    private JEditorPane editor;
    private JavaInterpreter interpreter;
    private List<String> history;
    private int historyPosition;
    private int editableStart;
    private String leadingText = "> ";
    
    private int ignoreKey = KeyEvent.SHIFT_DOWN_MASK;
    private boolean multiLineMode;
    
    public void setRightTab(RightTab rightTab)
    {
        editor = rightTab.getManualTestingPane();
        editor.setContentType(rightTab.getLanguage());
        Misc.setIntelligentKeyPair(editor);
        rightTab.getManualTestingScrollPane().setRowHeaderView(null);
        editor.addKeyListener(new InterpretiveKeyListener());
        resetOutput();
    }
    
    public InterpreterManager()
    {
        interpreter = new JavaInterpreter();
        history = new ArrayList();
    }
    
    public void setInterpreterClassLoader(ClassLoader loader)
    {
        interpreter = new JavaInterpreter(loader);
    }
    
    public void resetOutput()
    {
        editor.setText(leadingText);
        editableStart = leadingText.length();
    }
    
    private void addOutput(final String text)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
           @Override
           public void run()
           {
                editor.setText(editor.getText() + text.replaceAll("\r\n", "\n") + "\n" + leadingText);
                editableStart = editor.getText().length();
           }
        });
    }
    
    private void processOutput(String command) throws InterpreterException
    {
        synchronized (this) 
        {
            PrintStream oldPrintStream = System.out;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            System.setOut(new PrintStream(bos));
            try {
                String output = interpreter.interpret(command);
                addOutput(bos.toString() + output);
            } finally {
                System.setOut(oldPrintStream);
                try { bos.close(); } catch (IOException e) { e.printStackTrace(); }
            }
        }
    }
    
    private class InterpretiveKeyListener extends KeyAdapter
    {
        @Override
        public void keyPressed(KeyEvent evt)
        {
            switch (evt.getKeyCode())
            {
                //enter key
                case 10:
                    if (editor.getSelectionStart() < editableStart)
                    {
                        evt.consume();
                        break;
                    }
                    
                    if (multiLineMode || evt.getModifiersEx() == ignoreKey)
                    {
                        editor.setText(editor.getText() + "\n");
                        break;
                    }

                    final String command = editor.getText().substring(editableStart);
                    history.add(command);
                    historyPosition = history.size();

                    //clears the rest of the history
                    if (historyPosition < history.size())
                        history.subList(historyPosition, history.size()).clear();

                    new SwingWorker<Object, Void>(){
                        @Override
                        public Object doInBackground()
                        {
                            try {
                                processOutput(command);
                            } catch (InterpreterException e) {
                                if (e.getUserMessage().indexOf("Undefined class") >= 0)
                                    addOutput(e.getUserMessage() + 
                                            "(Press compile to update the interpreter to changes in the code.)");
                                else
                                    addOutput(e.getUserMessage());
                            }
                            return null;
                        }
                    }.execute();
                    break;

                //up arrow
                case 38:
                    if (multiLineMode || evt.getModifiersEx() == ignoreKey)
                        return;
                    
                    if (historyPosition > 0)
                        editor.setText(editor.getText().substring(0, editableStart) + history.get(--historyPosition));
                    evt.consume();
                    break;

                //down arrow
                case 40:
                    if (multiLineMode || evt.getModifiersEx() == ignoreKey)
                        return;
                    
                    if (historyPosition < history.size() - 1)
                        editor.setText(editor.getText().substring(0, editableStart) + history.get(++historyPosition));
                    else if (historyPosition == history.size() - 1)
                    {
                        editor.setText(editor.getText().substring(0, editableStart));
                        historyPosition++;
                    }
                    evt.consume();
                    break;
                    
                default:                  
                    int selStart = editor.getSelectionStart();
                    
                    if (selStart == editableStart && evt.getKeyCode() == 8)
                        evt.consume();
                    
                    if (selStart < editableStart)
                    {                        
                        switch (evt.getKeyCode())
                        {
                            case 37: //left
                            case 39: //right
                            case 35: //end
                                break;
                            default:
                                evt.consume();
                        }
                    }
                    
                    //HOME key pressed
                    if (selStart > editableStart && evt.getKeyCode() == 36)
                    {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run()
                            {
                                editor.setSelectionStart(Math.max(editableStart, editor.getSelectionStart()));                                
                            }
                        });
                    }
            }
        }
        
        @Override
        public void keyTyped(KeyEvent evt)
        {
            if (editor.getSelectionStart() < editableStart)
                evt.consume();
        }
    }
}
